package org.peoplesearch.storer;

import org.json.JSONException;
import org.peoplesearch.vk.api.androidvksdk.KException;
import org.peoplesearch.vk.api.androidvksdk.User;
import org.peoplesearch.vk.api.androidvksdk.WallMessage;
import org.peoplesearch.vk.processor.Credentials;
import org.peoplesearch.vk.processor.VkAPiExecutor;
import org.peoplesearch.vk.processor.VkCommand;
import org.peoplesearch.vk.utils.SerializableUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;


public class FileUsersStore {

    private VkAPiExecutor vkAPiExecutor;
    Logger logger = LoggerFactory.getLogger(this.getClass());

    public FileUsersStore(VkAPiExecutor vkAPiExecutor) {
        this.vkAPiExecutor = vkAPiExecutor;
    }

    public static void main(String[] args) throws InterruptedException {
        VkAPiExecutor executor = new VkAPiExecutor(new Credentials());
        FileUsersStore store = new FileUsersStore(executor);
        store.storeAllUsers("/data/all_true", "/data/temp");
    }


    public void storeAllUsers(String idFile, String resultDirectory) throws InterruptedException {
        logger.debug("idFile = " + idFile);
        List<Set<Long>> allIds = new ArrayList<Set<Long>>();
        Set<Long> ids = new HashSet<Long>();
        try {
            BufferedReader bufferedReader = new BufferedReader(new FileReader(new File(idFile)));
            int count = Integer.parseInt(bufferedReader.readLine());
            logger.debug("There are " + count + "id's in file");
            for (int i = 0; i < count; i++) {
                if (ids.size() >= 100) {
                    allIds.add(ids);
                    ids = new HashSet<Long>();
                }
                ids.add(Long.parseLong(bufferedReader.readLine()));
            }
            allIds.add(ids);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(-1);
        }

        BufferedWriter badBatches = null;
        try {
            badBatches = new BufferedWriter(new FileWriter(new File(resultDirectory + "badBatches.txt")));
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(-1);
        }

        final BufferedWriter finalBadBatches = badBatches;


        int batchNumber = 0;
        for (final Set<Long> idBatch : allIds) {
            logger.debug("Executing batch number " + batchNumber);
            final ArrayList<User> justMainInfos = new ArrayList<User>();
            final CountDownLatch latch = new CountDownLatch(1);
            final AtomicInteger ioExceptionNumber = new AtomicInteger(0);
            vkAPiExecutor.addCommand(new VkCommand() {
                @Override
                public void run() {
                    try {
                        justMainInfos.addAll(api.getProfiles(idBatch, null, null, null));
                        latch.countDown();
                    } catch (KException e) {
                        try {
                            finalBadBatches.write(e.toString());
                            for (Long uid : idBatch) {
                                finalBadBatches.write(uid + "\n");
                            }
                            finalBadBatches.flush();
                        } catch (IOException ie) {
                            ie.printStackTrace();
                            System.exit(-1);
                        }
                    } catch (JSONException e) {
                        e.printStackTrace();
                        System.exit(-1);
                    } catch (IOException e) {
                        if (ioExceptionNumber.incrementAndGet() > 100) {
                            e.printStackTrace();
                            System.exit(-1);
                        }
                        vkAPiExecutor.addCommand(this);
                    }
                }
            });
            latch.await();

            final AtomicInteger commandNumber = new AtomicInteger();
            final List<SerializableUser> serializableUsers = Collections.synchronizedList(new LinkedList<SerializableUser>());

            for (final User user : justMainInfos) {
                vkAPiExecutor.addCommand(new VkCommand() {
                    @Override
                    public void run() {
                        try {
                            int cNumber = commandNumber.getAndIncrement();
                            logger.debug("Commnand executing number " + cNumber);
                            final SerializableUser serializableUser = new SerializableUser(user);
                            serializableUsers.add(serializableUser);
                            int batchWallSize = 100;
                            int total = api.getWallMessagesCount(user.uid);
                            logger.debug(cNumber + " wall count is " + total);
                            int offset = 0;
                            while (offset < total) {
                                int getCount = batchWallSize;
                                if (total - offset < batchWallSize) {
                                    getCount = total - offset;
                                }
                                final int finalCount = getCount;
                                final int finalOffset = offset;
                                vkAPiExecutor.addCommand(new VkCommand() {
                                    @Override
                                    public void run() {
                                        try {
                                            int cNumber = commandNumber.getAndIncrement();
                                            logger.debug("Commnand executing number " + cNumber);
                                            serializableUser.wallMessages.addAll(api.getWallMessages(user.uid, finalCount, finalOffset));
                                        } catch (KException e) {
                                            serializableUser.wasProblems = true;
                                            serializableUser.e = e;
                                        } catch (JSONException e) {
                                            e.printStackTrace();
                                            System.exit(-1);
                                        } catch (IOException e) {
                                            if (ioExceptionNumber.incrementAndGet() > 100) {
                                                e.printStackTrace();
                                                System.exit(-1);
                                            }
                                            vkAPiExecutor.addCommand(this);
                                        }
                                    }
                                });
                                offset += batchWallSize;
                            }
                            logger.debug("Command ended " + cNumber);
                        } catch (Exception e) {
                            if ((e instanceof KException) && (((KException) e).error_code == 18))
                                return;
                            e.printStackTrace();
                            System.exit(-1);
                        }
                    }
                });
            }
            logger.debug("started to wait for completion");
            vkAPiExecutor.waitForCompletion();
            try {
                ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(new File(resultDirectory + "/" + batchNumber++)));
                outputStream.writeObject(serializableUsers.size());
                for (SerializableUser user : serializableUsers) {
                    outputStream.writeObject(user);
                }
                outputStream.flush();
                outputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(0);
            }


        }

    }


}
